
// Copyright (C) 2007 Richard Uhler
// This file is part of Esiply.
//
// Esiply is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License, Version 2,
// as published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include <iostream>
#include <vector>
#include <stdio.h>
#include "esni.h"

extern int yydebug;
extern int yy_flex_debug;
esni_interface_t* esni;
int debug;

esni_value_t eh_print(esni_context_t context)
{
    // Convert the first argument to a string, and print it
    esni_value_t str;
    esni->evaluate(context, "$1 = str;", &str, NULL);
    printf("%s", esni->get_string(context, str));
    esni_value_t und;
    esni->evaluate(context, "$1 = undefined;", &und, NULL);
    return und;
}

esni_value_t eh_debug(esni_context_t context)
{
    if (debug)
    {
        esni->evaluate(context, "print(str);", NULL, NULL);
    }
    esni_value_t und;
    esni->evaluate(context, "$1 = undefined;", &und, NULL);
    return und;
}

int esiply_main(const std::vector<char*>& scripts, const char* libdir,
        int argc, char* argv[], bool interactive)
{
    esni = esni_get_interface();

    // Initialize the interpreter
    esni_context_t context = esni->create(libdir);

    // Create the print function
    esni_value_t printfunc = esni->make_function(context, eh_print, "str");
    esni->evaluate(context, "print = $1;", &printfunc, NULL);

    // Add debug function so libraries work
    esni_value_t debugfunc = esni->make_function(context, eh_debug, "str");
    esni->evaluate(context, "debug = $1;", &debugfunc, NULL);

    // Add a system object.
    esni->evaluate(context, "System = {};", NULL, NULL);

    // Add argc 
    // We add one to reserve space for argv[0], which should point
    // to the script name being run.
    esni_value_t jsargc = esni->make_number(context, (double)(argc+1));
    esni->evaluate(context, "System.argc = $1;", &jsargc, NULL);

    // Add all of argv
    esni->evaluate(context, "System.argv = {};", NULL, NULL);
    assert(!scripts.empty());
    esni_value_t jsarg0 = esni->make_string(context, scripts[0]);
    esni->evaluate(context, "System.argv[0] = $1;", &jsarg0, NULL);
    for (int i = 0; i < argc; i++)
    {
        esni_value_t jsargv = esni->make_string(context, argv[i]);
        esni_value_t jsargi = esni->make_number(context, (double)(i+1));
        esni->evaluate(context, "System.argv[$1] = $2;", &jsargi, &jsargv);
    }

    if (yydebug)
    {
        fprintf(stderr, "STARTING MAIN JAVASCRIPT HERE\n");
    }

    if (interactive)
    {
        // TODO:
        // Print the copywrite notice (grep for  Gnomovision in COPYING)

        // We read in one line at a time, and execute it as soon as possible.
        std::string program;
        while (std::cin)
        {
            // prompt for input
            std::cout << "$ ";

            // Read in a line
            std::string line;
            getline(std::cin, line);

            // Append it to our program
            program += line;

            // Run the program.
            esni_value_t res = esni->evaluate(context, program.c_str(),
                    NULL, NULL);
            program = "";
        }
    }
    else
    {
        for (int i = 0; i < scripts.size(); i++)
        {
            esni->run_file(context, scripts[i]);
        }
    }
    return 0;
}

int main(int argc, char* argv[])
{
    // Parse the command line arguments. Command line args consist 
    // first of arguments to the interpreter, followed by a script
    // name (not starting with -) or just '-' which indicates use
    // stdin, followed by any arguments to be passed to the mode specific
    // initialization functions.
    yydebug = 0;
    yy_flex_debug = 0;
    std::vector<char*> scripts;
    char* libdir = NULL;
    bool interactive = false;

    int i;
    for (i = 1; i < argc; i++)
    {
        if (strcmp(argv[i], "--yydebug") == 0)
        {
            yydebug = 1;
        }
        else if (strcmp(argv[i], "--yyflexdebug") == 0)
        {
            yy_flex_debug = 1;
        }
        else if (strcmp(argv[i], "--debug") == 0)
        {
            debug = 1;
        }
        else if (i+1 < argc && strcmp(argv[i], "-f") == 0)
        {
            scripts.push_back(argv[++i]);
        }
        else if (i+1 < argc && strcmp(argv[i], "-L") == 0)
        {
            libdir = argv[++i];
        }
        else if (strcmp(argv[i], "-") == 0)
        {
            scripts.push_back("/dev/stdin");
        }
        else
        {
            scripts.push_back(argv[i]);
            break;
        }
    }
    
    // If no script given, use stdin
    if (scripts.empty())
    {
        interactive = true;
        scripts.push_back("/dev/stdin");
    }

    // Adjust argc and argv so that only the trailing arguments are left.
    argv += i;
    argc -= i;

    return esiply_main(scripts, libdir, argc, argv, interactive);
}

